package com.study.c6;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.format.DateTimeParseException;
import java.time.format.SignStyle;
import java.time.temporal.ChronoField;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class DateRegex
{
    private static final String SEPARATOR = "([-/.]?)";

    @SuppressWarnings("StringBufferReplaceableByString")
    public static boolean isValidDate(String dateStr)
    {
        StringBuilder regexBuilder = new StringBuilder();
        regexBuilder.append("^");                            // 匹配字符串的开头
        regexBuilder.append("(?:"); // （所有可能的四位数的年 - 所有年的月日 | 闰年的四位数字 - 2月 - 29 日）

        regexBuilder.append("(?!0000)[0-9]{4}");             // 年份
        regexBuilder.append(SEPARATOR);                     // 分隔符
        regexBuilder.append("(?:" +                                 // 所有年的月日
                "(?:0?[1-9]|1[0-2])\\1(?:0?[1-9]|1[0-9]|2[0-8])|" + // 所有月份都有 0-28 号
                "(?:0?[13-9]|1[0-2])\\1(?:29|30)|" +                // 除了 2 月的其它月份都有 29 和 30 号，平年的 2 月是 28 天
                "(?:0?[13578]|1[02])\\1(?:31)" +                    // 另外大月还有 31 号
                ")");
        regexBuilder.append("|");                            // 或者
        regexBuilder.append("(?:" +                                 // 闰年
                "[0-9]{2}(?:0[48]|[2468][048]|[13579][26])|" +
                "(?:0[48]|[2468][048]|[13579][26])00" +
                ")");
        regexBuilder.append(SEPARATOR);                     // 分隔符
        regexBuilder.append("0?2\\2(?:29)");                 // 只有闰年的 2 月有 29 号

        regexBuilder.append(")");                            // 关闭两种年的分组
        regexBuilder.append("$");                            // 匹配字符串的结尾

        String regex = regexBuilder.toString();
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(dateStr);
        return matcher.matches();
    }

    @SuppressWarnings("DuplicatedCode")
    public static boolean isValidDate1(String dateString) {
        String yearRegex = "(\\d{4})";
        String monthRegex = "(0?[1-9]|1[0-2])";
        String dayRegex = "(0?[1-9]|[12][0-9]|3[01])";
        String regex = String.format("^%s%s%s\\2%s$", yearRegex, SEPARATOR, monthRegex, dayRegex);
        Matcher matcher = Pattern.compile(regex)
                .matcher(dateString);
        if (!matcher.matches()) {
            return false;
        }
        int year = Integer.parseInt(matcher.group(1));
        int month = Integer.parseInt(matcher.group(3));
        int day = Integer.parseInt(matcher.group(4));

        return checkDayOfSmallMonth(year, month, day);
    }

    @SuppressWarnings("DuplicatedCode")
    public static boolean isValidDate2(String dateString) {
        String yearRegex = "(\\d{4})";
        String monthRegex = "(\\d{1,2})";
        String dayRegex = "(\\d{1,2})";
        String regex = String.format("^%s%s%s\\2%s$", yearRegex, SEPARATOR, monthRegex, dayRegex);
        Matcher matcher = Pattern.compile(regex)
                .matcher(dateString);
        if (!matcher.matches()) {
            return false;
        }
        int year = Integer.parseInt(matcher.group(1));
        int month = Integer.parseInt(matcher.group(3));
        int day = Integer.parseInt(matcher.group(4));
        if (month > 12) {
            return false;
        }
        if (day > 31) {
            return false;
        }
        return checkDayOfSmallMonth(year, month, day);
    }

    // 判断小月的日
    private static boolean checkDayOfSmallMonth(int year, int month, int day)
    {
        if (month == 2) {
            // 2 月单独判断
            if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) {
                // 闰年 29 天
                return day <= 29;
            } else {
                return day <= 28;
            }
        } else if (month == 4 || month == 6 || month == 9 || month == 11) {
            // 除 2 月外的小月
            return day <= 30;
        }
        return true;
    }

    static List<String> delimiters = Arrays.asList("-", "/", ".");
    static List<String> patterns = new ArrayList<>();

    static {
        for (String delimiter : delimiters) {
            patterns.add(String.join(delimiter, new String[]{"yyyy", "MM", "dd"}));
            patterns.add(String.join(delimiter, new String[]{"yyyy", "M", "d"}));
        }
        patterns.add(String.join("", new String[]{"yyyy", "MM", "dd"}));
    }

    public static boolean isValidDate3(String dateString)
    {
        for (String pattern : patterns) {
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
            try {
                // 检测不出小月，好像所有月都能有 31 号
                LocalDate localDate = LocalDate.parse(dateString, formatter);
                // 获取的 day 是不超过正确的天数
                int day = localDate.getDayOfMonth();
                return dateString.endsWith(String.valueOf(day));
            } catch (DateTimeParseException e) {
                // 尝试下一个分隔符
            }
        }
        return false;
    }

    public static boolean isValidDate4(String dateString)
    {
        DateTimeFormatterBuilder delimiterBuilder = new DateTimeFormatterBuilder();
        for (String delimiter : delimiters) {
            if (dateString.contains(delimiter)) {
                delimiterBuilder.appendLiteral(delimiter);
                break;
            }
        }
        DateTimeFormatter separatorFormatter = delimiterBuilder.toFormatter();

        DateTimeFormatterBuilder builder = new DateTimeFormatterBuilder();
        builder.appendValue(ChronoField.YEAR, 4)
                .append(separatorFormatter)
                .appendValue(ChronoField.MONTH_OF_YEAR, 1, 2, SignStyle.NORMAL)
                .append(separatorFormatter)
                .appendValue(ChronoField.DAY_OF_MONTH, 1, 2, SignStyle.NORMAL);
        DateTimeFormatter formatter = builder.toFormatter();

        try {
            LocalDate localDate = LocalDate.parse(dateString, formatter);
            // System.out.println("Date: " + localDate);
            int day = localDate.getDayOfMonth();
            return dateString.endsWith(String.valueOf(day));
        } catch (DateTimeParseException ignored) {
            return false;
        }
    }
}
